﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PMisuseAn.behaviour.model;
using PMisuseAn.util;
using System.Collections.Specialized;
using System.Collections;
using PMisuseAn.io;

namespace PMisuseAn.behaviour.adaptation
{
    public class AdaptationStratagy
    {
        static int noDefinedThreatOccurance;
        static int totalNoThreatOccurance;
        //static int noDefinedContextOccurance;
        //static int totalNoContextOccurance;
        static PolicyStatementAnalyser statAnalyser = new PolicyStatementAnalyser();
        static Stack<int> interactionSeqNos = HistorySerialisationEngine.getStackedInteractionList();

        public static List<Dictionary<Threat, string>> threatMitigationActions;
        //public static Dictionary<Threat, string> threatMitigationActions = new Dictionary<Threat, string>();

        public static List<Attribute> threathenedOperationalAttributes;
        public static string adaptationAction;
        public static List<double> maxUtilityFunctions;
        public static List<double> sensitivity_level_utilitys;
        public static List<double> obf_level_utilitys;
        public static double sensitivity_level_constant;
        public static double obf_level_constant;
        public static List<string> decisions;

        public static string adaptationRule;
        public static string metaAdaptationRule;
        public static List<double> threatFrequencys;
        public static List<List<string>> h_adaptationActionss;

        public static string getAdaptationStratagy(Threat threat)
        {
            if (threatMitigationActions == null)
            {
                threatMitigationActions = new List<Dictionary<Threat, string>>();
            }
            
            //setNoThreatOccurances(threat); //provides frequency of threat occurance
            setThreathenedAttributes(threat); //provides value of attribute and generality information
            //setContextChangeOccurances(threat); // provides value for frequency of change
           
            adaptationRule = getAdaptationRule();
            metaAdaptationRule = getAdaptationMetaRule();
            threatFrequencys = new List<double>();
            h_adaptationActionss = new List<List<string>>();

            decisions = new List<string>();
            maxUtilityFunctions = new List<double>();
            sensitivity_level_utilitys = new List<double>();
            obf_level_utilitys = new List<double>();

            foreach (Attribute att in threathenedOperationalAttributes)
            {                
                double attGen = 0;
                if (att.obfuscationLevel == Attribute.Obfuscation.InccurateImprecise)
                {
                    attGen = attGen + 1;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.AccurateImprecise)
                {
                    attGen = attGen + 0.8;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.InaccuratePrecise)
                {
                    attGen = attGen + 0.6;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.AccuratePrecise)
                {
                    attGen = attGen + 0.4;
                }

                attGen = attGen / threathenedOperationalAttributes.Count;
                double uThreatServerityGen = 0;
                for (int i = 1; i < AdaptationMgtConfigWindow.x_attGen.Count(); i++)
                {
                    if (AdaptationMgtConfigWindow.x_attGen[i - 1] <= attGen &&
                        AdaptationMgtConfigWindow.x_attGen[i] >= attGen)
                    {
                        double y_tf_i_minus_1 = AdaptationMgtConfigWindow.y_attGen[i - 1];
                        double y_tf_i = AdaptationMgtConfigWindow.y_attGen[i];
                        uThreatServerityGen = y_tf_i_minus_1 + (y_tf_i - y_tf_i_minus_1) *
                            ((attGen - AdaptationMgtConfigWindow.x_attGen[i - 1]) / (AdaptationMgtConfigWindow.x_attGen[i] - AdaptationMgtConfigWindow.x_attGen[i - 1])); //interpolate

                        obf_level_utilitys.Add(uThreatServerityGen);

                        break;
                    }
                }                

                double attValue = 0;
                if (att.sensitivityLevel == Attribute.Sensitivity.veryHigh)
                {
                    attValue = attValue + 1;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.high)
                {
                    attValue = attValue + 0.8;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.medium)
                {
                    attValue = attValue + 0.6;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.low)
                {
                    attValue = attValue + 0.4;
                }
                if (att.sensitivityLevel == Attribute.Sensitivity.veryLow)
                {
                    attValue = attValue + 0.2;
                }
                
                attValue = attValue / threathenedOperationalAttributes.Count;
                double uThreatServerityValue = 0;
                for (int i = 1; i < AdaptationMgtConfigWindow.x_AttVal.Count(); i++)
                {
                    if (AdaptationMgtConfigWindow.x_AttVal[i - 1] <= attValue &&
                        AdaptationMgtConfigWindow.x_AttVal[i] >= attValue)
                    {
                        double y_tf_i_minus_1 = AdaptationMgtConfigWindow.y_AttVal[i - 1];
                        double y_tf_i = AdaptationMgtConfigWindow.y_AttVal[i];
                        uThreatServerityValue = y_tf_i_minus_1 + (y_tf_i - y_tf_i_minus_1) *
                            ((attValue - AdaptationMgtConfigWindow.x_AttVal[i - 1]) / (AdaptationMgtConfigWindow.x_AttVal[i] - AdaptationMgtConfigWindow.x_AttVal[i - 1])); //interpolate

                        sensitivity_level_utilitys.Add(uThreatServerityValue);

                        break;
                    }
                }
                
                sensitivity_level_constant = AdaptationMgtConfigWindow.kv;
                obf_level_constant = AdaptationMgtConfigWindow.kg;

                //double expectedUtility = (AdaptationMgtConfigWindow.kcDelta * uThreatServerityCCF) + (AdaptationMgtConfigWindow.kv * uThreatServerityValue)
                //                            + (AdaptationMgtConfigWindow.kt * uThreatServerityTF) + (AdaptationMgtConfigWindow.kg * uThreatServerityGen);

                double expectedUtility = (AdaptationMgtConfigWindow.kv * uThreatServerityValue) + (AdaptationMgtConfigWindow.kg * uThreatServerityGen);

                double maxUtilValue = 0;
                foreach (double utilx in AdaptationMgtConfigWindow.y_attGen)
                {
                    if (utilx > maxUtilValue)
                    {
                        maxUtilValue = utilx;
                    }
                }
                foreach (double utilx in AdaptationMgtConfigWindow.y_AttVal)
                {
                    if (utilx > maxUtilValue)
                    {
                        maxUtilValue = utilx;
                    }
                }

                double expectedUtilityNormalised = (expectedUtility / maxUtilValue);
                maxUtilityFunctions.Add(expectedUtilityNormalised);

                string indAdaptationAction = "";

                if (threat.threatType == Threat.ThreatType.futureThreat)
                {
                    indAdaptationAction = "<potentialThreat>";
                }
                else if (expectedUtilityNormalised < AdaptationMgtConfigWindow.ignoreThreshExpectedUtility)
                {
                    indAdaptationAction = "IGNORE";
                }
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.preventThreshExpectedUtility
                    && expectedUtilityNormalised < AdaptationMgtConfigWindow.reactThreshExpectedUtility)
                {
                    indAdaptationAction = "REACT";
                }
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.ignoreThreshExpectedUtility
                    && expectedUtilityNormalised < AdaptationMgtConfigWindow.preventThreshExpectedUtility)
                {
                    indAdaptationAction = "PREVENT";
                }                
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.reactThreshExpectedUtility)
                {
                    indAdaptationAction = "TERMINATE";
                }

                decisions.Add(indAdaptationAction);
            }

            if (threat.threatType == Threat.ThreatType.futureThreat)
            {
                adaptationAction = "<potential>";
            }
            else if(decisions.Contains("TERMINATE"))
            {
                adaptationAction = "TERMINATE";
            }
            else if (decisions.Contains("PREVENT"))
            {
                adaptationAction = "PREVENT";
            }
            else if (decisions.Contains("REACT"))
            {
                adaptationAction = "REACT";
            }            
            else if (decisions.Contains("IGNORE"))
            {
                adaptationAction = "IGNORE";
            }

            //check meta adaptation rule
            //int threatFrequency = 1;
            List<string> h_adaptationActions = new List<string>();
            h_adaptationActions.Add(adaptationAction);

            foreach (Dictionary<Threat, string> threatMitigationAction in threatMitigationActions)
            {
                foreach (KeyValuePair<Threat, string> kvp in threatMitigationAction)
                {
                    Threat h_threat = kvp.Key;
                    string h_adaptationAction = kvp.Value;
                    bool sameState = false;
                    bool sameTransition = false;
                    bool sameAgents = false;
                    if (threat.message_s != null && h_threat.message_s != null)
                    {
                        if (threat.message_s.name.Equals(h_threat.message_s.name))
                        {
                            sameState = true;
                        }
                    }
                    else if (threat.message_t != null && h_threat.message_t != null)
                    {
                        if (threat.message_t.transitionName.Equals(h_threat.message_t.transitionName))
                        {
                            sameTransition = true;
                        }
                    }

                    if (threat.sendAgent.agentName.Equals(h_threat.sendAgent.agentName) &&
                        threat.subjectAgent.agentName.Equals(h_threat.subjectAgent.agentName) &&
                            threat.recieveAgent.agentName.Equals(h_threat.recieveAgent.agentName))
                    {
                        sameAgents = true;
                    }

                    if (sameAgents && (sameState || sameTransition))
                    {
                        //threatFrequency = threatFrequency + 1;
                        h_adaptationActions.Add(h_adaptationAction);
                    }
                }
            }            
            h_adaptationActionss.Add(h_adaptationActions);
            //threatFrequencys.Add(h_adaptationActions.Count);
            
            if (h_adaptationActions.Contains("<potential>"))
            {
                adaptationAction = "<potential>";
                double noPotential = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("<potential>"))
                    {
                        noPotential = noPotential + 1;
                    }
                }

                threatFrequencys.Add(noPotential);
            } 
            else if (h_adaptationActions.Contains("TERMINATE"))
            {
                adaptationAction = "TERMINATE";
                double noTerminates = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("TERMINATE"))
                    {
                        noTerminates = noTerminates + 1;
                    }
                }

                threatFrequencys.Add(noTerminates);
            }
            else if (h_adaptationActions.Contains("PREVENT"))
            {
                double noPrevents = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("PREVENT"))
                    {
                        noPrevents = noPrevents + 1;
                    }
                }
                if (noPrevents > AdaptationMgtConfigWindow.metaTerminateThreshExpectedUtility)
                {
                    adaptationAction = "TERMINATE";
                }
                threatFrequencys.Add(noPrevents);

            }

            else if (h_adaptationActions.Contains("REACT"))
            {
                double noReacts = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("REACT"))
                    {
                        noReacts = noReacts + 1;
                    }
                }
                if (noReacts > AdaptationMgtConfigWindow.metaPreventThreshExpectedUtility)
                {
                    adaptationAction = "PREVENT";
                }
                threatFrequencys.Add(noReacts);

            }

            else if (h_adaptationActions.Contains("IGNORE"))
            {
                double noIgnores = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("IGNORE"))
                    {
                        noIgnores = noIgnores + 1;
                    }
                }
                if (noIgnores > AdaptationMgtConfigWindow.metaReactThreshExpectedUtility)
                {
                    adaptationAction = "REACT";
                }
                threatFrequencys.Add(noIgnores);
            }
            Dictionary<Threat, string> threatMitigationAction_ = new Dictionary<Threat, string>();
            threatMitigationAction_.Add(threat, adaptationAction);
            
            threatMitigationActions.Add(threatMitigationAction_);

            return adaptationAction;            
        }

        public static string getAdaptationRule()
        {
            string adaptationRule = "IF U(VaGa) ≤" + AdaptationMgtConfigWindow.ignoreThreshExpectedUtility + " Then IGNORE" + "\r\n" +
                "IF " + AdaptationMgtConfigWindow.ignoreThreshExpectedUtility + "< U(VaGa) ≤" + AdaptationMgtConfigWindow.reactThreshExpectedUtility + " Then REACT" + "\r\n" +
                "IF " + AdaptationMgtConfigWindow.reactThreshExpectedUtility + "< U(VaGa) ≤" + AdaptationMgtConfigWindow.preventThreshExpectedUtility + " Then PREVENT" + "\r\n" +
                "IF U(VaGa) >" + AdaptationMgtConfigWindow.preventThreshExpectedUtility + " Then TERMINATE";

            return adaptationRule;
        }

        public static string getAdaptationMetaRule()
        {
            string adaptationMetaRule = "IF IGNORE and Tf >" + AdaptationMgtConfigWindow.metaReactThreshExpectedUtility + " Then REACT" + "\r\n" +
                    "IF REACT and Tf >" + AdaptationMgtConfigWindow.metaPreventThreshExpectedUtility + " Then PREVENT" + "\r\n" +
                    "IF PREVENT and Tf >" + AdaptationMgtConfigWindow.metaTerminateThreshExpectedUtility + " Then TERMINATE";

            return adaptationMetaRule;
        }

        private static void setThreathenedAttributes(Threat threat)
        {
            threathenedOperationalAttributes = null;
            if (threat.message_s != null)
            {
                threathenedOperationalAttributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
            }
            else if (threat.message_t != null)
            {
                threathenedOperationalAttributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
            }
        }

        private static void setNoThreatOccurances(Threat threat)
        {
            noDefinedThreatOccurance = 1;
            totalNoThreatOccurance = 1;
            foreach (Threat threatx in EmulationEnvironmentWindow.misuseAnalyser.threats)
            {
                totalNoThreatOccurance = totalNoThreatOccurance+1;

                if (threat.sendAgent.agentName.Equals(threatx.sendAgent.agentName)
                    && threat.recieveAgent.agentName.Equals(threatx.recieveAgent.agentName)
                    && threat.subjectAgent.agentName.Equals(threatx.subjectAgent.agentName))
                {
                    if (threat.message_s != null)
                    {
                        List<Attribute> operationalAttributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
                        bool contained = false;
                        foreach (Attribute operationalAtt in operationalAttributes)
                        {
                            if (threatx.message_s != null)
                            {
                                List<Attribute> operationalAttributesx = threatx.message_s.activeAction.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                        break;
                                    }
                                }
                            }
                            else if (threatx.message_t != null)
                            {
                                List<Attribute> operationalAttributesx = threatx.message_t.activeEvent.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                        break;
                                    }
                                }
                            }
                        }
                        if(contained == false)
                        {
                            continue;
                        }

                    }
                    else if (threat.message_t != null)
                    {
                        List<Attribute> operationalAttributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
                        bool contained = false;
                        foreach (Attribute operationalAtt in operationalAttributes)
                        {
                            if (threatx.message_s != null)
                            {
                                List<Attribute> operationalAttributesx = threatx.message_s.activeAction.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                        break;
                                    }
                                }
                            }
                            else if (threatx.message_t != null)
                            {
                                List<Attribute> operationalAttributesx = threatx.message_t.activeEvent.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                        break;
                                    }
                                }
                            }
                        }
                        if (contained == false)
                        {
                            continue;
                        }

                    }

                    noDefinedThreatOccurance = noDefinedThreatOccurance + 1;
                }
            }

            foreach (Threat threatx in EmulationEnvironmentWindow.aggregationMisuseAnalyser.aggregationThreats)
            {
                totalNoThreatOccurance = totalNoThreatOccurance+1;

                if (threat.sendAgent.agentName.Equals(threatx.sendAgent.agentName)
                    && threat.recieveAgent.agentName.Equals(threatx.recieveAgent.agentName)
                    && threat.subjectAgent.agentName.Equals(threatx.subjectAgent.agentName))
                {
                    if (threat.message_s != null)
                    {
                        List<Attribute> operationalAttributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
                        bool contained = false;
                        foreach (Attribute operationalAtt in operationalAttributes)
                        {
                            if (threatx.message_s != null)
                            {
                                List<Attribute> operationalAttributesx = threat.message_s.activeAction.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                    }
                                }
                            }
                            else if (threatx.message_t != null)
                            {
                                List<Attribute> operationalAttributesx = threat.message_t.activeEvent.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                    }
                                }
                            }
                        }
                        if(contained == false)
                        {
                            continue;
                        }

                    }
                    else if (threat.message_t != null)
                    {
                        List<Attribute> operationalAttributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
                        bool contained = false;
                        foreach (Attribute operationalAtt in operationalAttributes)
                        {
                            if (threatx.message_s != null)
                            {
                                List<Attribute> operationalAttributesx = threat.message_s.activeAction.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                    }
                                }
                            }
                            else if (threatx.message_t != null)
                            {
                                List<Attribute> operationalAttributesx = threat.message_t.activeEvent.parameterisedOperationalAttributes;
                                foreach (Attribute operationalAttx in operationalAttributesx)
                                {
                                    if (operationalAttx.name.Equals(operationalAtt.name))
                                    {
                                        contained = true;
                                    }
                                }
                            }
                        }
                        if (contained == false)
                        {
                            continue;
                        }

                    }

                    noDefinedThreatOccurance = noDefinedThreatOccurance + 1;
                }
            }
        }
    }
}
